/*
 * Copyright (C) 2020 Agrui
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <string.h>

#include <ArrayList.h>


static void deleteArrayListItem(ArrayListItem* item) {
    if (item) {
        if (item->mData) {
            free(item->mData);
            item->mData = NULL;
        }
        free(item);
    }
}

ArrayList* newArrayList() {
    ArrayList* list = (ArrayList*) malloc(sizeof(ArrayList));
    if (!list) {
        return NULL;
    }
    
    memset(list, 0, sizeof(ArrayList));

    list->mLock = newLock();
    if (!list->mLock) {
        free(list);
        return NULL;
    }

    return list;
}

ArrayListItem* newArrayListItem(void* data) {
    ArrayListItem* item = (ArrayListItem*) malloc(sizeof(ArrayListItem));
    if (!item) {
        return NULL;
    }

    memset(item, 0, sizeof(ArrayListItem));
    item->mData = data;
    return item;
}

void arrayListAdd(ArrayList* list, void* data) {
    ArrayListItem* item;

    if (!list) {
        return;
    }

    item = newArrayListItem(data);
    if (!item) {
        return;
    }

    item->mData = data;
    lock(list->mLock);

    if (!list->mHead) {
        list->mHead = item;
        list->mTail = item;
    } else {
        ArrayListItem* lastItem = list->mTail;
        item->mPre = lastItem;
        lastItem->mNext = item;
        list->mTail = item;
    }
    list->mSize++;
    unlock(list->mLock);
    
}

void arrayListRemove(ArrayList* list, void* data) {
    ArrayListItem* item;

    if (!list) {
        return;
    }

    lock(list->mLock);


    item = list->mHead;
    if (!item) {
        unlock(list->mLock);
        return;
    }

    do {
        if (item->mData == data) {
            if (item->mPre) {
                item->mPre->mNext = item->mNext;
            } else {
                list->mHead = item->mNext;
            }
            
            if (item->mNext) {
                item->mNext->mPre = item->mPre;
            } else {
                list->mTail = item->mPre;
            }
            deleteArrayListItem(item);
            list->mSize--;
            unlock(list->mLock);
            return;
        }
    } while(item = item->mNext);
    lock(list->mLock);
}

int arrayListSize(ArrayList* list) {
    if (list) {
        return list->mSize;
    }
    return 0;
}

void* arrayListGet(ArrayList* list, int index) {
    int i;
    ArrayListItem* item;
    if (!list || index< 0) {
        return NULL;
    }

    lock(list->mLock);

    if (index > list->mSize) {
        unlock(list->mLock);
        return NULL;
    }
    
    item = list->mHead;

    for (i=0; i<list->mSize && item!= NULL; i++) {
        if (i==index) {
            unlock(list->mLock);
            return item->mData;
        }
        item = item->mNext;
    }
    
    unlock(list->mLock);
    return NULL;
}

void arrayListClear(ArrayList* list) {
    lock(list->mLock);
    if (list) {
        ArrayListItem* item = list->mHead;
        ArrayListItem* nextItem;
        while(item) {
            nextItem = item->mNext;
            deleteArrayListItem(item);
            item = nextItem;
        }
        list->mHead = list->mTail = NULL;
        list->mSize = 0;
    }
    unlock(list->mLock);
}

void deleteArrayList(ArrayList* list) {
    if (list) {
        arrayListClear(list);
        deleteLock(list->mLock);
        free(list);
    }
}

